﻿using System;
using System.Collections;
using System.ComponentModel;
using System.Configuration.Install;
using System.IO;
using System.Reflection;
using System.Security;
using System.Xml;
using System.Xml.XPath;
using System.Security.AccessControl;
using System.Security.Permissions;

namespace TeamReview.SetupRegistrationAction
{
    [RunInstaller(true)]
    public class InstallerTeamReview : Installer
    {
        private const string AddinName = @"TeamReview.VSNetAddIn.Addin";
        private const string DestAddinName = @"TeamReview2010.VSNetAddIn.Addin";

        public override void Install(IDictionary stateSaver)
        {

            
            base.Install(stateSaver);
           
            Assembly asm = Assembly.GetExecutingAssembly();

            // get directory name of install directory
           
            string addInFileName = Path.Combine(Path.GetDirectoryName(asm.Location), AddinName);
            FileInfo addInFileInfo = new FileInfo(addInFileName);
            FileSecurity security = addInFileInfo.GetAccessControl();
            FileSystemAccessRule rule = new FileSystemAccessRule(Environment.UserName, FileSystemRights.FullControl, AccessControlType.Allow);
            security.SetAccessRule(rule);
            File.SetAccessControl(addInFileInfo.FullName, security);                
    

            if (addInFileInfo.Exists == false)
            {
                throw new FileNotFoundException(
                  string.Format("File '{0}' is not found. Installation will be aborted or not complete.", addInFileName));
           }

            try
            {
                
                XmlDocument doc = LoadAddInDocument(addInFileInfo);
                string folderPath = AddinFolderPath();
                CreateAddInDirectory(folderPath);
                DirectoryInfo addInDirectoryInfo = new DirectoryInfo(folderPath);
              

                bool modifiedSecurity = AddDirectorySecurity(addInDirectoryInfo.FullName, Environment.UserName, FileSystemRights.Write, AccessControlType.Allow);


                string destFile = Path.Combine(addInDirectoryInfo.FullName, DestAddinName);
				doc.Save(destFile);
				if (modifiedSecurity)
                    RemoveDirectorySecurity(addInDirectoryInfo.FullName, Environment.UserName, FileSystemRights.Write, AccessControlType.Allow);

            }
            catch (Exception ex)
            {
                throw new Exception(
                  string.Format(
                    "Failure to process file '{0}'. Error= {1}. Installation will be aborted or not complete.",
                    addInFileInfo.FullName, ex), ex);
            }
        }

        private static XmlDocument LoadAddInDocument(FileInfo fInfo)
        {
            // Load the .Addin file into XML document
            XmlDocument doc = new XmlDocument();
            doc.Load(fInfo.FullName);
            XPathNavigator navigator = doc.CreateNavigator();
            XmlNamespaceManager nsmgr = new XmlNamespaceManager(navigator.NameTable);
            nsmgr.AddNamespace("ae", "http://schemas.microsoft.com/AutomationExtensibility");

            XmlElement root = doc.DocumentElement;
            if (root == null) throw new XmlSyntaxException("Addin XML Root element definition");

            XmlNode assemblyNode = root.SelectSingleNode("/ae:Extensibility/ae:Addin/ae:Assembly", nsmgr);
            if (assemblyNode == null)
            {
                throw new XmlSyntaxException(
                  "XML File is missing the Assembly Element, installation is not complete");
            }

            assemblyNode.InnerText = Path.ChangeExtension(fInfo.FullName, "dll");
            return doc;
        }


        private static void CreateAddInDirectory(string folderPath)
        {
            DirectoryInfo parent = Directory.GetParent(folderPath);

            if (!parent.Exists)
                CreateAddInDirectory(parent.FullName);

            //get security on parent directory                   
            bool modifiedParentSecurity = AddDirectorySecurity(parent.FullName, Environment.UserName, FileSystemRights.Write, AccessControlType.Allow);
            Directory.CreateDirectory(folderPath);

            if (modifiedParentSecurity)
                RemoveDirectorySecurity(parent.FullName, Environment.UserName, FileSystemRights.Write, AccessControlType.Allow);


        }

        /// <summary>
        /// ["ALLUSERS"] is passed via the customactiondata in the setup project
        /// if ["ALLUSERS"] is not set or ="2", do a per user install
        /// see http://msdn.microsoft.com/en-us/library/aa367559(VS.85).aspx
        /// </summary>
        /// <returns>
        /// Path to C:\Documents and Settings\<user>\Application Data\Microsoft\MSEnvShared\Addins
        /// </returns>
        private string AddinFolderPath()
        {
			string alluser = this.Context.Parameters["ALLUSERS"];
			string folder = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData);
			if (string.IsNullOrEmpty(alluser) || (string.Compare(alluser, "2") == 0))
				folder = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);

			return string.Concat(folder, @"\Microsoft\MSEnvShared\Addins");
        }


		// Adds an ACL entry on the specified file for the specified account.
		public static bool AddDirectorySecurity(string dirName, string account, FileSystemRights rights, AccessControlType controlType)
		{


			// Get a FileSecurity object that represents the
			// current security settings.
			DirectorySecurity dSecurity = Directory.GetAccessControl(dirName);

			// Add the FileSystemAccessRule to the security settings.
			FileSystemAccessRule rule = new FileSystemAccessRule(account, rights, controlType);

			bool modified = false;
			dSecurity.ModifyAccessRule(AccessControlModification.Add, rule, out modified);
			// Set the new access settings.
			return modified;

		}

		// Removes an ACL entry on the specified file for the specified account.
		public static void RemoveDirectorySecurity(string dirName, string account, FileSystemRights rights, AccessControlType controlType)
		{

			// Get a FileSecurity object that represents the
			// current security settings.
			DirectorySecurity dSecurity = Directory.GetAccessControl(dirName);

			// Remove the FileSystemAccessRule from the security settings.
			dSecurity.RemoveAccessRule(new FileSystemAccessRule(account, rights, controlType));

			// Set the new access settings.
			Directory.SetAccessControl(dirName, dSecurity);

		}
        public override void Uninstall(IDictionary savedState)
        {
            base.Uninstall(savedState);

            try
            {
                string folderPath = AddinFolderPath();
                DirectoryInfo dirInfo = new DirectoryInfo(folderPath);
                if (dirInfo.Exists)
                {
                    string destFile = Path.Combine(folderPath, DestAddinName);
                    File.Delete(destFile);

                    File.Delete(Path.Combine(folderPath, @"TeamReview.config"));
                    File.Delete(Path.Combine(folderPath, @"TeamReview.log"));
                }
            }
            catch
            {
            }
        }
    }
}
